home *** CD-ROM | disk | FTP | other *** search
/ 130 MIDI Tool Box / 130 MIDI Tool Box.iso / mpu401 / mpu401.pcf < prev    next >
Text File  |  1986-06-24  |  16KB  |  610 lines

  1. *************************************************************************
  2. *                   MPU401.PCF                *
  3. *                                    *
  4. *    MIDI Record/Playback using Roland MPU401 MIDI Interface     *
  5. *                                    *
  6. *           Copyright Donald Swearingen, January 1985        *
  7. *         For private, non-commercial use only.            *
  8. *************************************************************************
  9.  
  10. Note: The following program segments are written in Laboratory Microsystems 
  11. PC/FORTH for the IBM PC. They were prepared using a standard text editor
  12. and compiled into the FORTH dictionary using the command:
  13.  
  14.             INCLUDE MPU401.PCF
  15. )
  16.  
  17. FORTH DEFINITIONS HEX
  18.  
  19. (
  20. ********************
  21. * Data Definitions *
  22. ********************
  23. )
  24. 0330    CONSTANT    MPU_DATA    ( IO Port Addresses for MPU_401)
  25. 0331    CONSTANT    MPU_STAT
  26. 0331    CONSTANT    MPU_CMND
  27.  
  28. 0010    CONSTANT    PGPH_SIZE    ( 8086 paragraph size )
  29. 1000    CONSTANT    T_SIZE        ( "Track" data buffer size )
  30.  
  31. 0008    CONSTANT    N_TK        ( Number of tracks )
  32.  
  33. CREATE    T_SEG        N_TK 2* ALLOT    ( segment pointers for track data )
  34. CREATE    T_RPTR        N_TK 2* ALLOT    ( Record data pointers )
  35. CREATE    T_PPTR        N_TK 2* ALLOT    ( Play data pointers )
  36. CREATE    T_RST        N_TK 2* ALLOT    ( Record/play running status )
  37. CREATE    T_NDAT        N_TK 2* ALLOT    ( # data bytes for current status )
  38.  
  39. VARIABLE        ACK_RCVD    ( Set to 1 when command ACK received )
  40. 2VARIABLE        PREV_IRQ2    ( original IRQ2 vector contents )
  41. VARIABLE        REC_TRK     ( Current record track 0-7 )
  42. VARIABLE        MPU_VEC     ( Current MPU character handler )
  43. VARIABLE        MPU_VEC_RST    ( Initial MPU character handler )
  44.  
  45. (
  46. ******************************
  47. * Auxiliary PC-DOS functions *
  48. ******************************
  49. )
  50.  
  51. ( free up memory paragraphs not used by FORTH
  52.   note: FORTH uses CS:0000 thru SS:FFFF 
  53. )
  54. CODE    FREE_MEM            ( --- n_avail )
  55.     ES        PUSH        ( Preserve registers )
  56.     CS        PUSH
  57.     AX        POP
  58.     ES, AX        MOV
  59.     BX, # FFFF    MOV        ( Request maximum )
  60.     AH, # 4A    MOV        ( SETBLOCK function )
  61.     21        INT        ( Get max. avail. in BX )
  62.     10$        JNC        ( Got all FFFF pgphs )
  63.     AX, # 8     CMP        ( Insufficient memory error?)
  64.     5$        JE        ( Yes, proceed )
  65.     BX, # 0     MOV        ( No pgphs available )
  66.     10$        JMP
  67. 5$:    SS        PUSH
  68.     CX        POP
  69.     CX, # 1000    ADD        ( CX = SS:FFFF+1 )
  70.     CS        PUSH
  71.     AX        POP        ( AX = CS:0000 )
  72.     ES, AX        MOV
  73.     CX, AX        SUB        ( CX = # pgphs used by FORTH )
  74.     BX, CX        SUB        ( BX = # pgphs not used by FORTH )
  75.     AH, # 4A    MOV        ( SETBLOCK function )
  76.     21        INT
  77.     10$        JNC
  78.     BX, # 0     MOV        ( error...return 0 )
  79. 10$:    ES        POP        ( restore register )
  80.     BX        PUSH        ( return # free pgphs )
  81.     NEXT,
  82. END-CODE
  83.  
  84. ( get memory paragraphs 
  85. CODE    GET_MEM             ( n_req --- seg n_alloc error_code )
  86.     BX        POP        ( # paragraphs requested )
  87.     AH, # 48    MOV        ( allocate memory function )
  88.     21        INT        ( DOS function interrupt ) 
  89.     10$        JC        ( Error return )
  90.     AX        PUSH        ( segment allocated )
  91.     BX        PUSH        ( # paragraphs allocated )
  92.     AX, # 0     MOV
  93.     AX        PUSH        ( no error )
  94.     NEXT,
  95. 10$:    CX, # 0     MOV
  96.     CX        PUSH        ( dummy segment )
  97.     BX        PUSH        ( # paragraphs actually available )
  98.     AX        PUSH        ( DOS error code )
  99.     NEXT,
  100. END-CODE
  101. (
  102. ********************
  103. * Output to MPU401 *
  104. ********************
  105. )
  106.  
  107. ( Wait until MPU401 is ready to receive data 
  108. )
  109. : MPU_TX_WAIT                ( --- )
  110.   BEGIN MPU_STAT PC@ 40 AND NOT UNTIL
  111. ;
  112. ( CALLable code version 
  113. )
  114. CODE    S_MPU_TX_WAIT 
  115.     DX, # MPU_STAT    MOV        ( MPU status port )
  116. 1$:    AL, DX        IN        ( read status )
  117.     AL, # 40    AND        ( wait for bit 6 = 0 )
  118.     1$        JNZ
  119.     RET
  120. END-CODE
  121.  
  122. ( Send command to MPU401
  123. )
  124. : !MPU_CMND                ( cmnd --- )
  125.   0 ACK_RCVD !                ( reset MPU ack flag ) 
  126.   MPU_TX_WAIT MPU_CMND PC!        ( send character )
  127.   BEGIN ACK_RCVD @ UNTIL        ( wait for ACK )
  128. ;
  129. ( Send command in AL to MPU401. CALLable code version. 
  130. )
  131. CODE    S_!MPU_CMND 
  132.     AX PUSH             ( save command )
  133.     ' S_MPU_TX_WAIT >BODY CALL      ( wait for tx ready )
  134.     AX, AX        XOR        ( AX = 0)
  135.     ACK_RCVD , AX    MOV        ( reset MPU ack flag )
  136.     DX, # MPU_CMND    MOV        ( MPU command port )
  137.     AX        POP        ( Retrieve command )
  138.     DX, AL        OUT        ( Send command )
  139. 1$:    AX, ACK_RCVD    MOV        ( Wait for command ACK )
  140.     AX, # FFFF    AND
  141.     1$        JZ    
  142.     RET
  143. END-CODE
  144.  
  145. ( Send data byte to MPU401 
  146. )
  147. : !MPU_DATA                ( data --- )
  148.   MPU_TX_WAIT MPU_DATA PC!  
  149. ;
  150. ( Send data byte in AL to MPU401. CALLable code version. 
  151. )
  152. CODE    S_!MPU_DATA            
  153.     AX        PUSH        ( save character ) 
  154.     ' S_MPU_TX_WAIT >BODY CALL      ( Wait for tx ready)
  155.     DX, # MPU_DATA    MOV        ( MPU Data port )
  156.     AX        POP        ( Retrieve data )
  157.     DX, AL        OUT        ( Send data )
  158.     RET
  159. END-CODE
  160.  
  161. (
  162. **************************
  163. * IRQ2 interrupt handler *
  164. **************************
  165. (
  166.  
  167. ( Begin interrupt: save registers and link to current character
  168.   handling vector 
  169. )
  170. CODE    IRQ2_INT    
  171.     STI                ( enable interrupts )
  172.     AX        PUSH        ( save environment )
  173.     BX        PUSH 
  174.     CX        PUSH
  175.     DX        PUSH 
  176.     DS        PUSH 
  177.     ES        PUSH
  178.     SI        PUSH
  179.     AX, CS        MOV        ( establish data accessibility )  
  180.     DS, AX        MOV  
  181.     DX, # MPU_DATA    MOV        ( read serial port)
  182.     AL, DX        IN
  183.     BX, MPU_VEC MOV
  184.     BX        JMP        ( jump to character handler )
  185. END-CODE
  186.  
  187. ( Signal end of interrupt and return from interrupt 
  188. )
  189. CODE    IRQ2_INT_END 
  190.     AL, # 20    MOV        ( send EOI to 8259 )
  191.     # 20 , AL    OUT
  192.     SI        POP        ( restore environment ) 
  193.     ES        POP  
  194.     DS        POP  
  195.     DX        POP 
  196.     CX        POP  
  197.     BX        POP  
  198.     AX        POP
  199.     IRET              
  200. END-CODE
  201.  
  202. (
  203. *****************************
  204. * Record/Playback functions *
  205. *****************************
  206. )
  207.  
  208. ( Determine the number of data bytes associated with the MIDI
  209.   status byte in CL and store for the currently active record 
  210.   or playback track [SI]. Input CL destroyed.
  211. )
  212. CODE    SET_T_NDAT
  213.     CL, # C0    CMP
  214.     1$        JB
  215.     CL, # DF    CMP
  216.     1$        JA
  217.     CX, # 1     MOV
  218.     T_NDAT [SI], CX MOV
  219.     RET
  220. 1$:    CX, # 2     MOV
  221.     T_NDAT [SI], CX MOV
  222.     RET
  223. END-CODE
  224.  
  225. ( place character in record buffer of current track
  226. )
  227. CODE    MPU_RECD_PUT 
  228.     CLI                ( clear interrupts for)
  229.                     ( pointer manipulation)
  230.     BX, T_SEG [SI]    MOV        ( get trk seg in ES )
  231.     ES, BX        MOV
  232.     BX, T_RPTR [SI] MOV        ( track record pointer )
  233.     BX, # T_SIZE    CMP        ( buffer full? )
  234.     1$        JB
  235.     STI                ( Yes...don't store )
  236.     RET
  237. 1$:    ES: [BX], AL    MOV        ( place char. in track buffer )
  238.     BX        INC        ( bump record pointer)
  239. 15$:    T_RPTR [SI], BX MOV        ( store updated ptr )
  240.     STI                ( enable interrupts)
  241.     RET
  242. END-CODE
  243.  
  244. ( record data bytes of current MIDI message
  245. )
  246. CODE    MPU_RECD_3
  247.     SI, REC_TRK    MOV        ( Get record trk in SI )
  248.     SI, # 1     SHL        ( Convert to word offset )
  249.     ' MPU_RECD_PUT >BODY CALL       ( store value )
  250.     T_NDAT [SI]    DEC
  251.     10$        JNZ
  252.     BX, MPU_VEC_RST MOV
  253.     MPU_VEC , BX    MOV        ( reset char vector )
  254. 10$:    ' IRQ2_INT_END >BODY JMP        ( leave )
  255. END-CODE
  256.  
  257. ( process second byte of current track event
  258. CODE    MPU_RECD_2
  259.     SI, REC_TRK    MOV        ( Get record trk in SI )
  260.     SI, # 1     SHL        ( Convert to word offset )
  261.     AL, # F8    CMP        ( check for MPU marks )
  262.     3$        JE
  263.     AL, # F9    CMP
  264.     3$        JE
  265.     AL, # FC    CMP
  266.     5$        JNE
  267. 3$:    ' MPU_RECD_PUT >BODY CALL       ( store mark )
  268.     BX, MPU_VEC_RST MOV
  269.     MPU_VEC , BX    MOV        ( reset char vector )
  270.     ' IRQ2_INT_END >BODY JMP
  271. 5$:    BX, # ' MPU_RECD_3 >BODY MOV    ( set char vec for MIDI data bytes )
  272.     MPU_VEC , BX    MOV
  273.     CL, T_RST [SI]    MOV
  274.     ' SET_T_NDAT >BODY CALL         ( compute # data bytes for status )
  275.     AL, # 80    CMP        ( new status?)
  276.     8$        JAE
  277.     ' MPU_RECD_3 >BODY JMP
  278. 8$:    T_RST [SI], AL    MOV        ( remember new running status)
  279.     ' MPU_RECD_PUT >BODY CALL       ( store value in track buffer )
  280.     CX, AX        MOV
  281.     ' SET_T_NDAT >BODY CALL         ( compute # data bytes for status )
  282.     ' IRQ2_INT_END >BODY JMP
  283. END-CODE
  284.  
  285. ( record timing value for current track event
  286. )
  287. CODE    MPU_RECD    
  288.     SI, REC_TRK    MOV        ( Get record trk in SI )
  289.     SI, # 1     SHL        ( Convert to word offset )
  290.     ' MPU_RECD_PUT >BODY CALL       ( Store timing value )
  291.     AL, # F8    CMP        ( timing overflow? )
  292.     1$        JE        ( Yes...one byte event )
  293.     BX, # ' MPU_RECD_2 >BODY MOV
  294.     MPU_VEC , BX    MOV        ( set char. vector for second byte )
  295. 1$:    ' IRQ2_INT_END >BODY JMP
  296. END-CODE
  297.  
  298. ( handle play request 
  299. )
  300. CODE    MPU_PLAY
  301.     AX, # 07    AND        ( ensure trk 0-7)
  302.     AX, # 1     SHL        ( cnvt to word offset )
  303.     SI, AX        MOV
  304.     BX, T_PPTR [SI] MOV
  305.     CX, T_RPTR [SI] MOV
  306.     BX, CX        CMP        ( Anything left ?)
  307.     1$        JB        ( Yes...send next event )
  308.     AL, # 0     MOV        ( No...send data end FC )
  309.     ' S_!MPU_DATA >BODY CALL
  310.     AL, # FC    MOV
  311.     ' S_!MPU_DATA >BODY CALL
  312.     10$        JMP
  313. 1$:    AX, T_SEG [SI]    MOV        ( Get trk seg in ES )
  314.     ES, AX        MOV
  315.     ES: AL, [BX]    MOV        ( Get timing value )
  316.     ' S_!MPU_DATA >BODY CALL        ( send timing value )
  317.     BX        INC
  318.     AL, # F8    CMP        ( Timing value = 240? )
  319.     10$        JE        ( Yes, EXIT )
  320.     ES: AL, [BX]    MOV        ( Check next character )
  321.     AL, # F8    CMP        ( MPU mark?)
  322.     3$        JE
  323.     AL, # F9    CMP
  324.     3$        JE
  325.     AL, # FC    CMP
  326.     5$        JNE        ( Not mark )
  327. 3$:    BX        INC        ( "read" mark )
  328.     ' S_!MPU_DATA >BODY CALL        ( send mark )
  329.     10$        JMP        ( EXIT )
  330. 5$:    AL, # 80    CMP        ( New status? )
  331.     7$        JB
  332.     T_RST [SI], AL    MOV        ( Reset running status )
  333.     BX        INC        ( "read" status )
  334.     ' S_!MPU_DATA >BODY CALL        ( send status )
  335. 7$:    CL, T_RST [SI]    MOV
  336.     ' SET_T_NDAT >BODY CALL
  337. 9$:    ES: AL, [BX]    MOV        ( Read next data byte )
  338.     BX        INC
  339.     ' S_!MPU_DATA >BODY CALL        ( send data byte )
  340.     T_NDAT [SI]    DEC
  341.     9$        JNZ
  342. 10$:    T_PPTR [SI], BX MOV        ( Reset play pointer )
  343.     ' IRQ2_INT_END >BODY JMP
  344. END-CODE
  345.  
  346. (
  347. *************************
  348. * Interpret MPU message *
  349. *************************
  350. )
  351.  
  352. ( Begin processing a new MPU message
  353. )
  354. CODE    MPU_MSG
  355.     AL, # FE    CMP        ( command ack? )
  356.     1$        JNE
  357.     ACK_RCVD , AX    MOV        ( Yes, signal ack received )
  358.     ' IRQ2_INT_END >BODY JMP        ( leave )
  359. 1$:    AL, # EF    CMP        ( timing value? )
  360.     4$        JA
  361.     ' MPU_RECD >BODY JMP            ( Yes, record data )
  362. 4$:    AL, # F8    CMP        ( timing overflow? )
  363.     7$        JNE
  364.     ' MPU_RECD >BODY JMP            ( Yes, record data )    
  365. 7$:    AL, # F7    CMP        ( Play request? )
  366.     10$        JA
  367.     ' MPU_PLAY >BODY JMP            ( Yes, play data )
  368. 10$:    ' IRQ2_INT_END >BODY JMP                ( Ignore F9-FF )
  369. END-CODE
  370.  
  371. (
  372. ***************************
  373. * Interrupt control words *
  374. ***************************
  375. )
  376.  
  377. ( save previous values and initialize IRQ2 interrupt vectors
  378. )
  379. : IRQ2_TRAP                ( --- )
  380.   ['] MPU_MSG >BODY DUP
  381.   MPU_VEC ! MPU_VEC_RST !
  382.   0 28 2@L  PREV_IRQ2 2!        ( save old IRQ2 vector )
  383.   ?CS: 0 2A !L                ( segment of handler )
  384.   ['] IRQ2_INT >BODY 0 28 !L            ( offset of handler )
  385. ;
  386. ( restore previous IRQ2 vectors
  387. )
  388. : IRQ2_REL                ( --- )
  389.   PREV_IRQ2 2@ 0 28 2!L
  390. ;
  391. ( enable interrupts on IRQ2
  392. )
  393. : IRQ2_ENABLE                ( --- )
  394.   21 PC@ 0FB AND 21 PC!         ( 8259 int mask IRQ2 )
  395. ;         
  396. ( disable IRQ2 interrupts
  397. )
  398. : IRQ2_DISABLE                ( --- )
  399.   21 PC@ 04 OR 21 PC!            ( mask IRQ2 interrupt )
  400. ;
  401. ( enable interrupts, set up vectors 
  402. )
  403. : IRQ2_ON                ( --- )
  404.   IRQ2_TRAP IRQ2_ENABLE 
  405. ;
  406. ( disable interrupts, release vectors 
  407. )
  408. : IRQ2_OFF                ( --- )
  409.   IRQ2_DISABLE    IRQ2_REL 
  410. ;
  411. (
  412. ************************
  413. * MPU401 control words *
  414. ************************
  415. )
  416.  
  417. : RECD+     22 !MPU_CMND ;        ( start record process )
  418. : RECD-     11 !MPU_CMND ;        ( stop record process )
  419. : PLAY+     0A !MPU_CMND ;        ( start playback process )
  420. : PLAY-     05 !MPU_CMND ;        ( stop playback process )
  421. : OVDB+     2A !MPU_CMND ;        ( start overdub process )
  422. : OVDB-     15 !MPU_CMND ;        ( stop overdub process )
  423. : MPU_RESET    FF !MPU_CMND ;        ( system reset )
  424. : THRU-     33 !MPU_CMND ;        ( disable MIDI thru function )
  425. : RT_OUT-    32 !MPU_CMND ;        ( disable MIDI real-time output ) 
  426. : T_BYTE+    34 !MPU_CMND ;        ( enable leading timing byte in
  427.                       stop mode )
  428. : RT_TO_HOST+    39 !MPU_CMND ;        ( enable real-time to host )
  429. : INT_CLK    80 !MPU_CMND ;        ( use internal clock )
  430. : FSK_CLK    81 !MPU_CMND ;        ( use FSK clock from tape )
  431. : MIDI_CLK    82 !MPU_CMND ;        ( use MIDI clock )
  432. : MET_ON    83 !MPU_CMND ;        ( metronome on )
  433. : MET_OFF    84 !MPU_CMND ;        ( metronome off )
  434. : MET_ON_ACC    85 !MPU_CMND ;        ( metronome on with accent )
  435. : BENDER-    86 !MPU_CMND ;        ( allow bender data )
  436. : BENDER+    87 !MPU_CMND ;        ( screen bender data )
  437. : MIDI_THRU-    88 !MPU_CMND ;        ( enable MIDI thru )
  438. : MIDI_THRU+    89 !MPU_CMND ;        ( disable MIDI thru )
  439. : DATA_IN_STP-    8A !MPU_CMND ;        ( disable data in stop mode )
  440. : DATA_IN_STP+    8B !MPU_CMND ;        ( enable data in stop mode )
  441. : MEAS_END-    8C !MPU_CMND ;        ( enable meas. end to host )
  442. : MEAS_END+    8D !MPU_CMND ;        ( disable meas. end to host )
  443. : COND-     8E !MPU_CMND ;        ( conductor off )
  444. : COND+     8F !MPU_CMND ;        ( conductor on )
  445. : RT_AFF-    90 !MPU_CMND ;        ( disable real time affection )
  446. : RT_AFF+    91 !MPU_CMND ;        ( enable real time affection )
  447. : FSK<INT    92 !MPU_CMND ;        ( sync FSK to internal clock )
  448. : FSK<MIDI    93 !MPU_CMND ;        ( sync FSK to MIDI clock )
  449. : CLK-        94 !MPU_CMND ;        ( disable clock to host )
  450. : CLK+        95 !MPU_CMND ;        ( enable clock to host )
  451. : SYSX-     96 !MPU_CMND ;        ( disable system exclusive data )
  452. : SYSX+     97 !MPU_CMND ;        ( enable system exclusive data )
  453. : TMPO_RST    B1 !MPU_CMND ;        ( reset relative tempo )
  454. : CLR_PLY_CTR    B8 !MPU_CMND ;        ( clear play counters )
  455. : CLR_PLY_TAB    B9 !MPU_CMND ;        ( clear play table )
  456. : CLR_REC_CTR    BA !MPU_CMND ;        ( clear record counter )
  457.  
  458. ( request to send MIDI data on specified track
  459. : RTS_DATA                ( trk --- )
  460.   D0 + !MPU_CMND 
  461. ;
  462. ( request to send system exclusive message
  463. )
  464. : RTS_SYSX                ( --- )
  465.   DF !MPU_CMND 
  466. ;
  467. ( set tempo 
  468. )
  469. : SET_TEMPO                ( beats/min --- )
  470.   E0 !MPU_CMND !MPU_DATA 
  471. ;
  472. ( set relative tempo where 40H = 1/1 ratio
  473. )
  474. : REL_TEMPO                ( rel --- ) 
  475.   E1 !MPU_CMND !MPU_DATA 
  476. ;
  477. ( set rate of tempo change, 0 = immediate, 1 = slowest, FF = fastest
  478. )
  479. : GRADUATION                ( grad --- )
  480.   E2 !MPU_CMND !MPU_DATA 
  481. ;
  482. ( send clock to host every rate/4 internal clocks
  483. )
  484. : CLK>HST_RATE                ( rate    --- )
  485.   E7 !MPU_CMND !MPU_DATA 
  486. ;
  487. ( set active playback tracks using 8 bit map
  488. )
  489. : SET_AC_TRK                ( trks --- )
  490.   EC !MPU_CMND !MPU_DATA 
  491. ;
  492. ( send play counter of track when switched from playback to record mode
  493. )
  494. : SND_PLY_CTR+                ( trks --- )
  495.   ED  !MPU_CMND !MPU_DATA 
  496. ;
  497. ( initialize state of MPU_401 
  498. )
  499. : MPU_INIT                ( --- )
  500.   MPU_RESET                ( reset MPU-401 )
  501.   THRU- MIDI_THRU-            ( THRU off )
  502.   T_BYTE+                ( enable leading timing byte)
  503.   BENDER-                ( pitch bend data off )
  504.   DATA_IN_STP-                ( data in STOP off )
  505.   MEAS_END-                ( send measure end off )
  506.   RT_AFF-                ( real-time affection off )
  507.   SYSX-                 ( exclusive to host off )
  508.   CLK-                    ( clock to host off )
  509.   COND-                 ( conductor off )
  510.   60 CLK>HST_RATE            ( Set clock to host to MIDI )
  511.                     ( standard: 24 clks/beat )
  512.   64 SET_TEMPO                ( 100 beats per minute )
  513. ;
  514. ( enable interrupts and initialize MPU401
  515. )
  516. : MPU_ON                ( --- )
  517.   IRQ2_ON
  518.   MPU_INIT
  519. ;
  520. ( disable MPU401 activity
  521. )
  522. : MPU_OFF                ( --- )
  523.   IRQ2_OFF
  524. ;
  525. (
  526. *********************************
  527. * Record/playback control words *
  528. *********************************
  529. )
  530.  
  531. ( reset record pointer for track n
  532. )
  533. : RESET_RPTR                ( n --- )
  534.   2* T_RPTR + 0 SWAP !
  535. ;
  536. ( reset play pointer for track n
  537. )
  538. : RESET_PPTR                ( n --- )
  539.   2* T_PPTR + 0 SWAP !
  540. ;
  541. ( intialize state of record/playback variables and obtain memory 
  542.   for record/play data buffers 
  543. )
  544. : RECORD_INIT                ( --- )
  545.   FREE_MEM DROP
  546.   T_SIZE PGPH_SIZE / N_TK *        ( --- n_required_pgphs )
  547.   GET_MEM                ( --- seg n_avail_pgphs err_code )
  548.   0<> IF
  549.     2DROP
  550.     ." Not enough memory for record/playback " CR 
  551.     EXIT
  552.   THEN
  553.   DROP                    ( --- seg )
  554.   T_SIZE PGPH_SIZE /            ( --- seg pgphs/trk )
  555.   N_TK 0 DO                ( set up pointers to track buffers )
  556.     2DUP I * +
  557.     T_SEG I 2 * + !
  558.   LOOP
  559.   2DROP
  560.   N_TK 0 DO                ( reset record/playback pointers )
  561.     I RESET_PPTR
  562.     I RESET_RPTR
  563.   LOOP
  564. ;
  565. ( record MIDI data on trk n
  566. )
  567. : RECORD                ( n --- )
  568.   7 AND REC_TRK !            ( set record track )
  569.   REC_TRK @ RESET_RPTR            ( reset record pointer)
  570.   RECD+                 ( Start record )
  571. ;
  572. : RECORD_OFF    
  573.   RECD-
  574. ;
  575. ( overdub MIDI data on trk n while playing other tracks
  576. )
  577. : OVERDUB                ( n --- )
  578.   7 AND REC_TRK !            ( set record track )
  579.   1 REC_TRK @ SHIFT NOT 
  580.   SET_AC_TRK                ( all trks active except REC_TRK )
  581.   CLR_PLY_CTR                ( clear play counters )
  582.   REC_TRK @ RESET_RPTR            ( reset record pointer)
  583.   N_TK 0 DO                ( reset play pointers )
  584.     I RESET_PPTR
  585.   LOOP
  586.   OVDB+                 ( Start record )
  587. ;
  588. : OVERDUB_OFF    
  589.   OVDB- 
  590. ;
  591. ( play midi data for all tracks
  592. )
  593. : PLAY                    ( --- )
  594.   N_TK 0 DO                ( reset play pointers )
  595.     I RESET_PPTR
  596.   LOOP
  597.   FF SET_AC_TRK             ( all trks active )
  598.   CLR_PLY_CTR
  599.   PLAY+                 ( start play )
  600. ;
  601. : PLAY_OFF
  602.   PLAY- 
  603. ;
  604.  
  605. DECIMAL
  606.